1 Initialise params

library(scMET)
library(data.table)
library(matrixStats)
library(coda)
library(ggpubr)
library(purrr)
set.seed(1234)

data_dir <- "~/datasets/scMET_ms/synthetic/diff_var/data/rep1/"
out_dir <- "~/datasets/scMET_ms/synthetic/shrinkage/"
if (!dir.exists(out_dir)) {dir.create(out_dir, recursive = TRUE)}

opts <- list()
opts$N_feat <- 300
opts$N_cells <- c(20, 50, 200)
opts$N_cpgs <- c(8, 15, 50)
opts$OR_change_gamma <- 2

1.1 Load fitted objects and perform MLE

summary_stats <- data.table(cells = numeric(), features = numeric(), 
                            cpgs = numeric(), or_gamma = numeric(), 
                            feature_name = character(), gamma_true = numeric(), 
                            gamma_median = numeric(), mu_true = numeric(), 
                            mu_median = numeric(), gamma_mle = numeric(), 
                            mu_mle = numeric(), cpg_cov = numeric())
for (f in opts$N_feat) {
  for (cpg in opts$N_cpgs) {
    for (c in opts$N_cells) {
      for (or in opts$OR_change_gamma) {
        dt <- readRDS(file = paste0(data_dir, "scmet_ORgamma", or, "_feat", f, 
                                    "_cells", c, "_cpgs", cpg, "_mcmcFALSE.rds"))
        fit_obj <- dt[[paste0("scmet_A")]]
        sim_dt <- dt$sim_dt[[paste0("scmet_dt_A")]]
        
        mle <- sim_dt$Y[, bb_mle(cbind(total_reads, met_reads))[c("gamma", "mu")], 
                        by = c("Feature")]
        summary_stats <- rbind(summary_stats,
            data.table(cells = c,
                       features = f,
                       cpgs = cpg,
                       or_gamma = or,
                       feature_name = fit_obj$feature_names,
                       # gamma
                       gamma_true = sim_dt$theta_true$gamma,
                       gamma_median = colMedians(fit_obj$posterior$gamma),
                       # mu
                       mu_true = sim_dt$theta_true$mu,
                       mu_median = colMedians(fit_obj$posterior$mu),
                       # MLE estimates
                       gamma_mle = mle$gamma,
                       mu_mle = mle$mu, 
                       # CpG coverage per feature
                       cpg_cov = sim_dt$Y[, median(total_reads), 
                                          by = "Feature"]$V1 ) )
      }
    }
  }
}
rm(mle, fit_obj, sim_dt, dt, f, cpg, c, or)

2 Overdispersion shrinkage as we increase cells

tmp <- copy(summary_stats)
tmp$cells <- factor(tmp$cells, levels = unique(tmp$cells), 
                        labels = paste(unique(tmp$cells), "cells"))
#tmp$gamma_median[tmp$gamma_median > 0.6] <- 0.58
#tmp$gamma_mle[tmp$gamma_mle > 0.6] <- 0.58
gg_overdisp <- list()
iter <- 1
for (cpg in opts$N_cpgs) {
  dt <- tmp[features == opts$N_feat & cpgs == cpg]
  dt <- dt %>% split(., by = "cells") %>%
    map(~ .[sample(150)]) %>%
    rbindlist
  if (cpg == 8) {
    gg_overdisp[[iter]] <- shrinkage_overdisp_plot(dt, x_lab = "True overdispersion (CpG poor, average 5 CpGs)")
  }else if (cpg == 15) {
    gg_overdisp[[iter]] <- shrinkage_overdisp_plot(dt, x_lab = "True overdispersion (CpG moderate, average 10 CpGs)")
  } else {
    gg_overdisp[[iter]] <- shrinkage_overdisp_plot(dt, x_lab = "True overdispersion (CpG rich, average 30 CpGs)") +
            theme(legend.position = "none")
  }
  iter <- iter + 1
}
print(cowplot::plot_grid(plotlist = gg_overdisp, nrow = 3, 
                         labels = c("a", "b", "c"), label_size = 18))

2.1 Mean methylation shrinkage as we increase cells

tmp <- copy(summary_stats)
tmp$cells <- factor(tmp$cells, levels = unique(tmp$cells), 
                        labels = paste(unique(tmp$cells), "cells"))
gg_mean <- list()
iter <- 1
for (cpg in opts$N_cpgs) {
  dt <- tmp[features == opts$N_feat & cpgs == cpg]
  dt <- dt %>% split(., by = "cells") %>%
    map(~ .[sample(150)]) %>%
    rbindlist
  if (cpg == 8) {
    gg_mean[[iter]] <- shrinkage_mean_plots(dt, x_lab = "True mean methylation (CpG poor, average 5 CpGs)")
  }else if (cpg == 15) {
    gg_mean[[iter]] <- shrinkage_mean_plots(dt, x_lab = "True mean methylation (CpG moderate, average 10 CpGs)")
  } else {
    gg_mean[[iter]] <- shrinkage_mean_plots(dt, x_lab = "True mean methylation (CpG rich, average 30 CpGs)") +
            theme(legend.position = "none")
  }
  iter <- iter + 1
}
print(cowplot::plot_grid(plotlist = gg_mean, nrow = 3, 
                         labels = c("a", "b", "c"), label_size = 18))

3 Model performance in predicting true values

3.1 Log odds ratio difference between infered and true values for \(\gamma\).

opts$val_thresh <- 1e-2
tmp <- copy(summary_stats) %>% .[, c("features", "or_gamma", "cpg_cov") := NULL]
to_plot <- tmp[, c("cells", "cpgs", "feature_name", "gamma_true", 
                   "gamma_median", "gamma_mle")] %>%
  .[, gamma_mle := ifelse(gamma_mle > opts$val_thresh & 
                            gamma_mle < 1 - opts$val_thresh, gamma_mle, 
                          ifelse(gamma_mle < opts$val_thresh, 
                                 opts$val_thresh, 1 - opts$val_thresh))] %>%
  .[, scmet_lor := abs(scMET:::.compute_log_odds_ratio(gamma_true, gamma_median))] %>%
  .[, mle_lor := abs(scMET:::.compute_log_odds_ratio(gamma_true, gamma_mle))] %>%
  .[, c("gamma_true", "gamma_median", "gamma_mle", "feature_name") := NULL] %>%
  melt(id.vars = c("cells", "cpgs"))

to_plot$cpgs <- factor(to_plot$cpgs, levels = c(8, 15, 50), 
                       labels = c("CpG poor", "CpG moderate", "CpG rich"))
to_plot$variable <- factor(to_plot$variable, levels = c("mle_lor", "scmet_lor"), 
                           labels = c("BB MLE", "scMET"))

gg_overdisp_lor <- ggboxplot(to_plot, x = "cells", y = "value", 
                             fill = "variable", lwd = 0.4, outlier.size = 0.5) +
  facet_wrap(~cpgs, scales = "fixed", nrow = 3) +
  scale_fill_manual(values = c( "#999999", "#E69F00", "#56B4E9")) +
  labs(title = NULL, x = "Number of cells", 
       y = expression(paste("LOR (", gamma[true], ", ", 
                            gamma[estimated], ")")), fill = "Model") +
  theme_classic() +
  ylim(c(0, 1.8)) +
  theme(
      legend.position = "top",
      legend.title = element_blank(),
      legend.margin = margin(-2, 0, -2, 0),
      legend.box.margin = margin(-2, 0, -2, 0),
      panel.spacing.y = unit(3, "lines"),
      plot.tag = element_text(color = "black", face = "bold", size = rel(1.6)),
      legend.text = element_text(color = "black", size = rel(1.1)),
      strip.text = element_text(color = "black", size = rel(1.1)),
      axis.text = element_text(color = "black", size = rel(0.8)),
      axis.title = element_text(color = "black", size = rel(1.1))
    )
print(gg_overdisp_lor)

3.2 LORs between infered and true values for \(\mu\).

opts$val_thresh <- 1e-10
tmp <- copy(summary_stats) %>% .[, c("features", "or_gamma", "cpg_cov") := NULL]
to_plot <- tmp[, c("cells", "cpgs", "feature_name", 
                   "mu_true", "mu_median", "mu_mle")] %>%
  .[, mu_mle := ifelse(mu_mle > opts$val_thresh & 
                         mu_mle < 1 - opts$val_thresh, mu_mle, 
                       ifelse(mu_mle < opts$val_thresh, 
                              opts$val_thresh, 1 - opts$val_thresh))] %>%
  .[, mle_lor := abs(scMET:::.compute_log_odds_ratio(mu_true, mu_mle))] %>%
  .[, scmet_lor := abs(scMET:::.compute_log_odds_ratio(mu_true, mu_median))] %>%
  .[, c("mu_true", "mu_median", "mu_mle", "feature_name") := NULL] %>%
  melt(id.vars = c("cells", "cpgs"))

to_plot$cpgs <- factor(to_plot$cpgs, levels = c(8, 15, 50), 
                       labels = c("CpG poor", "CpG moderate", "CpG rich"))
to_plot$variable <- factor(to_plot$variable, levels = c("mle_lor", "scmet_lor"), 
                           labels = c("BB MLE", "scMET"))

gg_mean_lor <- ggboxplot(to_plot, x = "cells", y = "value", 
                         fill = "variable", lwd = 0.4, outlier.size = 0.5) +
  facet_wrap(~cpgs, scales = "fixed", nrow = 3) +
  scale_fill_manual(values = c( "#999999", "#E69F00", "#56B4E9")) +
  labs(title = NULL, x = "Number of cells", 
       y = expression(paste("LOR (", mu[true], ", ", 
                            mu[estimated], ")")), fill = "Model") +
  theme_classic() +
  ylim(c(0, 1.8)) +
  theme(
      legend.position = "top",
      legend.title = element_blank(),
      legend.margin = margin(-2, 0, -2, 0),
      legend.box.margin = margin(-2, 0, -2, 0),
      panel.spacing.y = unit(3, "lines"),
      plot.tag = element_text(color = "black", face = "bold", size = rel(1.6)),
      legend.text = element_text(color = "black", size = rel(1.1)),
      strip.text = element_text(color = "black", size = rel(1.1)),
      axis.text = element_text(color = "black", size = rel(0.8)),
      axis.title = element_text(color = "black", size = rel(1.1))
    )
print(gg_mean_lor)

4 Final plots

library(patchwork)
gg_gamma <- ((gg_overdisp[[1]]/gg_overdisp[[2]]/gg_overdisp[[3]]) | gg_overdisp_lor) + 
  plot_layout(widths = c(3.7, 1.2), heights = c(1.08, 1)) +
  plot_annotation(tag_levels = 'a')
print(gg_gamma)

pdf(file = paste0(out_dir, "shrinkage_overdisp.pdf"), width = 10, height = 9)
print(gg_gamma)
dev.off()

gg_mu <- ((gg_mean[[1]]/gg_mean[[2]]/gg_mean[[3]]) | gg_mean_lor) + 
  plot_layout(widths = c(3.7, 1.2), , heights = c(1.08, 1)) +
  plot_annotation(tag_levels = 'a')
print(gg_mu)

pdf(file = paste0(out_dir, "shrinkage_mean.pdf"), width = 10, height = 9)
print(gg_mu)
dev.off()

LS0tCnRpdGxlOiAiU2hyaW5rYWdlIG9mIHBhcmFtZXRlciBlc3RpbWF0ZXMgZm9yIHZhcnlpbmcgc2FtcGxlIHNpemVzIgphdXRob3I6ICJDLkEuIEthcG91cmFuaSAmIFIuIEFyZ2VsYWd1ZXQiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICBoaWdobGlnaHQ6IGhhZGRvY2sKICAgIHRoZW1lOiBjZXJ1bGVhbgogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0b2M6IHllcwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCByZXN1bHRzID0gImhpZGUiKQoKIyBTaHJpbmthZ2UgcGxvdCBmb3Igb3ZlcmRpc3BlcnNpb24gcGFyYW1ldGVyCnNocmlua2FnZV9vdmVyZGlzcF9wbG90IDwtIGZ1bmN0aW9uKHJlcywgeF9sYWIgPSAiVHJ1ZSBvdmVyZGlzcGVyc2lvbiIpewogIGNvbG9ycyA8LSBjKCJCQiBNTEUiID0gIiM5OTk5OTkiLCAic2NNRVQiID0gIiNFNjlGMDAiKQogIGdnIDwtIGdncGxvdChyZXMpICsKICAgIGdlb21fcG9pbnQoYWVzKHkgPSBnYW1tYV9tbGUsIHggPSBnYW1tYV90cnVlLCBmaWxsID0gIkJCIE1MRSIpLCAKICAgICAgICAgICAgICAgc2hhcGUgPSAyMSwgc2l6ZSA9IDEuMiwgYWxwaGEgPSAwLjUsIHN0cm9rZSA9IDAuMikgKwogICAgZ2VvbV9wb2ludChhZXMoeSA9IGdhbW1hX21lZGlhbiwgeCA9IGdhbW1hX3RydWUsIGZpbGwgPSAic2NNRVQiKSwgCiAgICAgICAgICAgICAgIHNoYXBlID0gMjEsIHNpemUgPSAxLjksIHN0cm9rZSA9IDAuMikgKwogICAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBjb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwgYWxwaGEgPSAwLjcpICsKICAgIGdlb21fc2VnbWVudChhZXMoeCA9IGdhbW1hX3RydWUsIHkgPSBnYW1tYV9tbGUsIAogICAgICAgICAgICAgICAgICAgICB4ZW5kID0gZ2FtbWFfdHJ1ZSwgeWVuZCA9IGdhbW1hX21lZGlhbiksCiAgICAgICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3coYW5nbGUgPSAxMCwgbGVuZ3RoID0gdW5pdCgwLjA2LCAiaW5jaGVzIikpLCAKICAgICAgICAgICAgICAgICBzaXplID0gMC4wNSwgYWxwaGEgPSAwLjMpICsKICAgIGZhY2V0X3dyYXAofmNlbGxzLCBzY2FsZXMgPSAiZnJlZV94IiwgbnJvdyA9IDEpICsKICAgIGxhYnMoeCA9IHhfbGFiLCB5ID0gIkVzdGltYXRlZCBvdmVyZGlzcGVyc2lvbiIsIGNvbG9yID0gIkxlZ2VuZCIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbG9ycykgKwogICAgdGhlbWVfY2xhc3NpYygpICsKICAgIHRoZW1lKAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQubWFyZ2luID0gbWFyZ2luKC0yLCAwLCAtMiwgMCksCiAgICAgICAgbGVnZW5kLmJveC5tYXJnaW4gPSBtYXJnaW4oLTIsIDAsIC0yLCAwKSwKICAgICAgICBwYW5lbC5zcGFjaW5nLnggPSB1bml0KDAuOSwgImxpbmVzIiksCiAgICAgICAgcGxvdC50YWcgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBmYWNlID0gImJvbGQiLCBzaXplID0gcmVsKDEuNikpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IHJlbCgxLjEpKSwKICAgICAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IHJlbCgxLjEpKSwKICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBzaXplID0gcmVsKDAuOCkpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBzaXplID0gcmVsKDEuMSkpCiAgICAgICkKICByZXR1cm4oZ2cpCn0KCgojIFNocmlua2FnZSBwbG90IGZvciBtZWFuIHBhcmFtZXRlcgpzaHJpbmthZ2VfbWVhbl9wbG90cyA8LSBmdW5jdGlvbihyZXMsIHhfbGFiID0gIlRydWUgbWVhbiBtZXRoeWxhdGlvbiIpewogIGNvbG9ycyA8LSBjKCJCQiBNTEUiID0gIiM5OTk5OTkiLCAic2NNRVQiID0gIiNFNjlGMDAiKQogIGdnIDwtIGdncGxvdChyZXMpICsKICAgIGdlb21fcG9pbnQoYWVzKHkgPSBtdV9tbGUsIHggPSBtdV90cnVlLCBmaWxsID0gIkJCIE1MRSIpLCAKICAgICAgICAgICAgICAgc2hhcGUgPSAyMSwgc2l6ZSA9IDEuMiwgYWxwaGEgPSAwLjUsIHN0cm9rZSA9IDAuMikgKwogICAgZ2VvbV9wb2ludChhZXMoeSA9IG11X21lZGlhbiwgeCA9IG11X3RydWUsIGZpbGwgPSAic2NNRVQiKSwgCiAgICAgICAgICAgICAgIHNoYXBlID0gMjEsIHNpemUgPSAxLjksIHN0cm9rZSA9IDAuMikgKwogICAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBjb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwgYWxwaGEgPSAwLjcpICsKICAgIGdlb21fc2VnbWVudChhZXMoeCA9IG11X3RydWUsIHkgPSBtdV9tbGUsIAogICAgICAgICAgICAgICAgICAgICB4ZW5kID0gbXVfdHJ1ZSwgeWVuZCA9IG11X21lZGlhbiksCiAgICAgICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3coYW5nbGUgPSAxMCwgbGVuZ3RoID0gdW5pdCgwLjA2LCAiaW5jaGVzIikpLCAKICAgICAgICAgICAgICAgICBzaXplID0gMC4wNSwgYWxwaGEgPSAwLjMpICsKICAgIGZhY2V0X3dyYXAofmNlbGxzLCBzY2FsZXMgPSAiZnJlZV94IiwgbnJvdyA9IDEpICsKICAgIGxhYnMoeCA9IHhfbGFiLCB5ID0gIkVzdGltYXRlZCBtZWFuIG1ldGh5bGF0aW9uIiwgY29sb3IgPSAiTGVnZW5kIikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sb3JzKSArCiAgICAjIHhsYWIoIlRydWUgb3ZlcmRpc3BlcnNpb24iKSArIHlsYWIoIkVzdGltYXRlZCBvdmVyZGlzcGVyc2lvbiIpICsKICAgIHRoZW1lX2NsYXNzaWMoKSArCiAgICB0aGVtZSgKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwgI2MoMC44NywgMC4xMyksICMicmlnaHQiLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQubWFyZ2luID0gbWFyZ2luKC0yLCAwLCAtMiwgMCksCiAgICAgICAgbGVnZW5kLmJveC5tYXJnaW4gPSBtYXJnaW4oLTIsIDAsIC0yLCAwKSwKICAgICAgICBwYW5lbC5zcGFjaW5nLnggPSB1bml0KDEsICJsaW5lcyIpLAogICAgICAgIHBsb3QudGFnID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgZmFjZSA9ICJib2xkIiwgc2l6ZSA9IHJlbCgxLjYpKSwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIHNpemUgPSByZWwoMS4xKSksCiAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIHNpemUgPSByZWwoMS4xKSksCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IHJlbCgwLjgpKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IHJlbCgxLjEpKQogICAgICApCiAgcmV0dXJuKGdnKQp9CgpgYGAKCgojIEluaXRpYWxpc2UgcGFyYW1zCmBgYHtyfQpsaWJyYXJ5KHNjTUVUKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkobWF0cml4U3RhdHMpCmxpYnJhcnkoY29kYSkKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkocHVycnIpCnNldC5zZWVkKDEyMzQpCgpkYXRhX2RpciA8LSAifi9kYXRhc2V0cy9zY01FVF9tcy9zeW50aGV0aWMvZGlmZl92YXIvZGF0YS9yZXAxLyIKb3V0X2RpciA8LSAifi9kYXRhc2V0cy9zY01FVF9tcy9zeW50aGV0aWMvc2hyaW5rYWdlLyIKaWYgKCFkaXIuZXhpc3RzKG91dF9kaXIpKSB7ZGlyLmNyZWF0ZShvdXRfZGlyLCByZWN1cnNpdmUgPSBUUlVFKX0KCm9wdHMgPC0gbGlzdCgpCm9wdHMkTl9mZWF0IDwtIDMwMApvcHRzJE5fY2VsbHMgPC0gYygyMCwgNTAsIDIwMCkKb3B0cyROX2NwZ3MgPC0gYyg4LCAxNSwgNTApCm9wdHMkT1JfY2hhbmdlX2dhbW1hIDwtIDIKYGBgCgojIyBMb2FkIGZpdHRlZCBvYmplY3RzIGFuZCBwZXJmb3JtIE1MRQpgYGB7cn0Kc3VtbWFyeV9zdGF0cyA8LSBkYXRhLnRhYmxlKGNlbGxzID0gbnVtZXJpYygpLCBmZWF0dXJlcyA9IG51bWVyaWMoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjcGdzID0gbnVtZXJpYygpLCBvcl9nYW1tYSA9IG51bWVyaWMoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlX25hbWUgPSBjaGFyYWN0ZXIoKSwgZ2FtbWFfdHJ1ZSA9IG51bWVyaWMoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBnYW1tYV9tZWRpYW4gPSBudW1lcmljKCksIG11X3RydWUgPSBudW1lcmljKCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbXVfbWVkaWFuID0gbnVtZXJpYygpLCBnYW1tYV9tbGUgPSBudW1lcmljKCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbXVfbWxlID0gbnVtZXJpYygpLCBjcGdfY292ID0gbnVtZXJpYygpKQpmb3IgKGYgaW4gb3B0cyROX2ZlYXQpIHsKICBmb3IgKGNwZyBpbiBvcHRzJE5fY3BncykgewogICAgZm9yIChjIGluIG9wdHMkTl9jZWxscykgewogICAgICBmb3IgKG9yIGluIG9wdHMkT1JfY2hhbmdlX2dhbW1hKSB7CiAgICAgICAgZHQgPC0gcmVhZFJEUyhmaWxlID0gcGFzdGUwKGRhdGFfZGlyLCAic2NtZXRfT1JnYW1tYSIsIG9yLCAiX2ZlYXQiLCBmLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIl9jZWxscyIsIGMsICJfY3BncyIsIGNwZywgIl9tY21jRkFMU0UucmRzIikpCiAgICAgICAgZml0X29iaiA8LSBkdFtbcGFzdGUwKCJzY21ldF9BIildXQogICAgICAgIHNpbV9kdCA8LSBkdCRzaW1fZHRbW3Bhc3RlMCgic2NtZXRfZHRfQSIpXV0KICAgICAgICAKICAgICAgICBtbGUgPC0gc2ltX2R0JFlbLCBiYl9tbGUoY2JpbmQodG90YWxfcmVhZHMsIG1ldF9yZWFkcykpW2MoImdhbW1hIiwgIm11IildLCAKICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJGZWF0dXJlIildCiAgICAgICAgc3VtbWFyeV9zdGF0cyA8LSByYmluZChzdW1tYXJ5X3N0YXRzLAogICAgICAgICAgICBkYXRhLnRhYmxlKGNlbGxzID0gYywKICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGYsCiAgICAgICAgICAgICAgICAgICAgICAgY3BncyA9IGNwZywKICAgICAgICAgICAgICAgICAgICAgICBvcl9nYW1tYSA9IG9yLAogICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVfbmFtZSA9IGZpdF9vYmokZmVhdHVyZV9uYW1lcywKICAgICAgICAgICAgICAgICAgICAgICAjIGdhbW1hCiAgICAgICAgICAgICAgICAgICAgICAgZ2FtbWFfdHJ1ZSA9IHNpbV9kdCR0aGV0YV90cnVlJGdhbW1hLAogICAgICAgICAgICAgICAgICAgICAgIGdhbW1hX21lZGlhbiA9IGNvbE1lZGlhbnMoZml0X29iaiRwb3N0ZXJpb3IkZ2FtbWEpLAogICAgICAgICAgICAgICAgICAgICAgICMgbXUKICAgICAgICAgICAgICAgICAgICAgICBtdV90cnVlID0gc2ltX2R0JHRoZXRhX3RydWUkbXUsCiAgICAgICAgICAgICAgICAgICAgICAgbXVfbWVkaWFuID0gY29sTWVkaWFucyhmaXRfb2JqJHBvc3RlcmlvciRtdSksCiAgICAgICAgICAgICAgICAgICAgICAgIyBNTEUgZXN0aW1hdGVzCiAgICAgICAgICAgICAgICAgICAgICAgZ2FtbWFfbWxlID0gbWxlJGdhbW1hLAogICAgICAgICAgICAgICAgICAgICAgIG11X21sZSA9IG1sZSRtdSwgCiAgICAgICAgICAgICAgICAgICAgICAgIyBDcEcgY292ZXJhZ2UgcGVyIGZlYXR1cmUKICAgICAgICAgICAgICAgICAgICAgICBjcGdfY292ID0gc2ltX2R0JFlbLCBtZWRpYW4odG90YWxfcmVhZHMpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAiRmVhdHVyZSJdJFYxICkgKQogICAgICB9CiAgICB9CiAgfQp9CnJtKG1sZSwgZml0X29iaiwgc2ltX2R0LCBkdCwgZiwgY3BnLCBjLCBvcikKYGBgCgoKIyBPdmVyZGlzcGVyc2lvbiBzaHJpbmthZ2UgYXMgd2UgaW5jcmVhc2UgY2VsbHMKYGBge3IsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTQuNX0KdG1wIDwtIGNvcHkoc3VtbWFyeV9zdGF0cykKdG1wJGNlbGxzIDwtIGZhY3Rvcih0bXAkY2VsbHMsIGxldmVscyA9IHVuaXF1ZSh0bXAkY2VsbHMpLCAKICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcGFzdGUodW5pcXVlKHRtcCRjZWxscyksICJjZWxscyIpKQojdG1wJGdhbW1hX21lZGlhblt0bXAkZ2FtbWFfbWVkaWFuID4gMC42XSA8LSAwLjU4CiN0bXAkZ2FtbWFfbWxlW3RtcCRnYW1tYV9tbGUgPiAwLjZdIDwtIDAuNTgKZ2dfb3ZlcmRpc3AgPC0gbGlzdCgpCml0ZXIgPC0gMQpmb3IgKGNwZyBpbiBvcHRzJE5fY3BncykgewogIGR0IDwtIHRtcFtmZWF0dXJlcyA9PSBvcHRzJE5fZmVhdCAmIGNwZ3MgPT0gY3BnXQogIGR0IDwtIGR0ICU+JSBzcGxpdCguLCBieSA9ICJjZWxscyIpICU+JQogICAgbWFwKH4gLltzYW1wbGUoMTUwKV0pICU+JQogICAgcmJpbmRsaXN0CiAgaWYgKGNwZyA9PSA4KSB7CiAgICBnZ19vdmVyZGlzcFtbaXRlcl1dIDwtIHNocmlua2FnZV9vdmVyZGlzcF9wbG90KGR0LCB4X2xhYiA9ICJUcnVlIG92ZXJkaXNwZXJzaW9uIChDcEcgcG9vciwgYXZlcmFnZSA1IENwR3MpIikKICB9ZWxzZSBpZiAoY3BnID09IDE1KSB7CiAgICBnZ19vdmVyZGlzcFtbaXRlcl1dIDwtIHNocmlua2FnZV9vdmVyZGlzcF9wbG90KGR0LCB4X2xhYiA9ICJUcnVlIG92ZXJkaXNwZXJzaW9uIChDcEcgbW9kZXJhdGUsIGF2ZXJhZ2UgMTAgQ3BHcykiKQogIH0gZWxzZSB7CiAgICBnZ19vdmVyZGlzcFtbaXRlcl1dIDwtIHNocmlua2FnZV9vdmVyZGlzcF9wbG90KGR0LCB4X2xhYiA9ICJUcnVlIG92ZXJkaXNwZXJzaW9uIChDcEcgcmljaCwgYXZlcmFnZSAzMCBDcEdzKSIpICsKICAgICAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQogIH0KICBpdGVyIDwtIGl0ZXIgKyAxCn0KcHJpbnQoY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gZ2dfb3ZlcmRpc3AsIG5yb3cgPSAzLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoImEiLCAiYiIsICJjIiksIGxhYmVsX3NpemUgPSAxOCkpCmBgYAoKIyMgTWVhbiBtZXRoeWxhdGlvbiBzaHJpbmthZ2UgYXMgd2UgaW5jcmVhc2UgY2VsbHMKYGBge3IsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTQuNX0KdG1wIDwtIGNvcHkoc3VtbWFyeV9zdGF0cykKdG1wJGNlbGxzIDwtIGZhY3Rvcih0bXAkY2VsbHMsIGxldmVscyA9IHVuaXF1ZSh0bXAkY2VsbHMpLCAKICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcGFzdGUodW5pcXVlKHRtcCRjZWxscyksICJjZWxscyIpKQpnZ19tZWFuIDwtIGxpc3QoKQppdGVyIDwtIDEKZm9yIChjcGcgaW4gb3B0cyROX2NwZ3MpIHsKICBkdCA8LSB0bXBbZmVhdHVyZXMgPT0gb3B0cyROX2ZlYXQgJiBjcGdzID09IGNwZ10KICBkdCA8LSBkdCAlPiUgc3BsaXQoLiwgYnkgPSAiY2VsbHMiKSAlPiUKICAgIG1hcCh+IC5bc2FtcGxlKDE1MCldKSAlPiUKICAgIHJiaW5kbGlzdAogIGlmIChjcGcgPT0gOCkgewogICAgZ2dfbWVhbltbaXRlcl1dIDwtIHNocmlua2FnZV9tZWFuX3Bsb3RzKGR0LCB4X2xhYiA9ICJUcnVlIG1lYW4gbWV0aHlsYXRpb24gKENwRyBwb29yLCBhdmVyYWdlIDUgQ3BHcykiKQogIH1lbHNlIGlmIChjcGcgPT0gMTUpIHsKICAgIGdnX21lYW5bW2l0ZXJdXSA8LSBzaHJpbmthZ2VfbWVhbl9wbG90cyhkdCwgeF9sYWIgPSAiVHJ1ZSBtZWFuIG1ldGh5bGF0aW9uIChDcEcgbW9kZXJhdGUsIGF2ZXJhZ2UgMTAgQ3BHcykiKQogIH0gZWxzZSB7CiAgICBnZ19tZWFuW1tpdGVyXV0gPC0gc2hyaW5rYWdlX21lYW5fcGxvdHMoZHQsIHhfbGFiID0gIlRydWUgbWVhbiBtZXRoeWxhdGlvbiAoQ3BHIHJpY2gsIGF2ZXJhZ2UgMzAgQ3BHcykiKSArCiAgICAgICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKICB9CiAgaXRlciA8LSBpdGVyICsgMQp9CnByaW50KGNvd3Bsb3Q6OnBsb3RfZ3JpZChwbG90bGlzdCA9IGdnX21lYW4sIG5yb3cgPSAzLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoImEiLCAiYiIsICJjIiksIGxhYmVsX3NpemUgPSAxOCkpCmBgYAoKCiMgTW9kZWwgcGVyZm9ybWFuY2UgaW4gcHJlZGljdGluZyB0cnVlIHZhbHVlcwojIyBMb2cgb2RkcyByYXRpbyBkaWZmZXJlbmNlIGJldHdlZW4gaW5mZXJlZCBhbmQgdHJ1ZSB2YWx1ZXMgZm9yICRcZ2FtbWEkLgpgYGB7ciwgZmlnLndpZHRoPTIsIGZpZy5oZWlnaHQ9My41fQpvcHRzJHZhbF90aHJlc2ggPC0gMWUtMgp0bXAgPC0gY29weShzdW1tYXJ5X3N0YXRzKSAlPiUgLlssIGMoImZlYXR1cmVzIiwgIm9yX2dhbW1hIiwgImNwZ19jb3YiKSA6PSBOVUxMXQp0b19wbG90IDwtIHRtcFssIGMoImNlbGxzIiwgImNwZ3MiLCAiZmVhdHVyZV9uYW1lIiwgImdhbW1hX3RydWUiLCAKICAgICAgICAgICAgICAgICAgICJnYW1tYV9tZWRpYW4iLCAiZ2FtbWFfbWxlIildICU+JQogIC5bLCBnYW1tYV9tbGUgOj0gaWZlbHNlKGdhbW1hX21sZSA+IG9wdHMkdmFsX3RocmVzaCAmIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2FtbWFfbWxlIDwgMSAtIG9wdHMkdmFsX3RocmVzaCwgZ2FtbWFfbWxlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ2FtbWFfbWxlIDwgb3B0cyR2YWxfdGhyZXNoLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3B0cyR2YWxfdGhyZXNoLCAxIC0gb3B0cyR2YWxfdGhyZXNoKSldICU+JQogIC5bLCBzY21ldF9sb3IgOj0gYWJzKHNjTUVUOjo6LmNvbXB1dGVfbG9nX29kZHNfcmF0aW8oZ2FtbWFfdHJ1ZSwgZ2FtbWFfbWVkaWFuKSldICU+JQogIC5bLCBtbGVfbG9yIDo9IGFicyhzY01FVDo6Oi5jb21wdXRlX2xvZ19vZGRzX3JhdGlvKGdhbW1hX3RydWUsIGdhbW1hX21sZSkpXSAlPiUKICAuWywgYygiZ2FtbWFfdHJ1ZSIsICJnYW1tYV9tZWRpYW4iLCAiZ2FtbWFfbWxlIiwgImZlYXR1cmVfbmFtZSIpIDo9IE5VTExdICU+JQogIG1lbHQoaWQudmFycyA9IGMoImNlbGxzIiwgImNwZ3MiKSkKCnRvX3Bsb3QkY3BncyA8LSBmYWN0b3IodG9fcGxvdCRjcGdzLCBsZXZlbHMgPSBjKDgsIDE1LCA1MCksIAogICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkNwRyBwb29yIiwgIkNwRyBtb2RlcmF0ZSIsICJDcEcgcmljaCIpKQp0b19wbG90JHZhcmlhYmxlIDwtIGZhY3Rvcih0b19wbG90JHZhcmlhYmxlLCBsZXZlbHMgPSBjKCJtbGVfbG9yIiwgInNjbWV0X2xvciIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiQkIgTUxFIiwgInNjTUVUIikpCgpnZ19vdmVyZGlzcF9sb3IgPC0gZ2dib3hwbG90KHRvX3Bsb3QsIHggPSAiY2VsbHMiLCB5ID0gInZhbHVlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9ICJ2YXJpYWJsZSIsIGx3ZCA9IDAuNCwgb3V0bGllci5zaXplID0gMC41KSArCiAgZmFjZXRfd3JhcCh+Y3Bncywgc2NhbGVzID0gImZpeGVkIiwgbnJvdyA9IDMpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCAiIzk5OTk5OSIsICIjRTY5RjAwIiwgIiM1NkI0RTkiKSkgKwogIGxhYnModGl0bGUgPSBOVUxMLCB4ID0gIk51bWJlciBvZiBjZWxscyIsIAogICAgICAgeSA9IGV4cHJlc3Npb24ocGFzdGUoIkxPUiAoIiwgZ2FtbWFbdHJ1ZV0sICIsICIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2FtbWFbZXN0aW1hdGVkXSwgIikiKSksIGZpbGwgPSAiTW9kZWwiKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB5bGltKGMoMCwgMS44KSkgKwogIHRoZW1lKAogICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwKICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICBsZWdlbmQubWFyZ2luID0gbWFyZ2luKC0yLCAwLCAtMiwgMCksCiAgICAgIGxlZ2VuZC5ib3gubWFyZ2luID0gbWFyZ2luKC0yLCAwLCAtMiwgMCksCiAgICAgIHBhbmVsLnNwYWNpbmcueSA9IHVuaXQoMywgImxpbmVzIiksCiAgICAgIHBsb3QudGFnID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgZmFjZSA9ICJib2xkIiwgc2l6ZSA9IHJlbCgxLjYpKSwKICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBzaXplID0gcmVsKDEuMSkpLAogICAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IHJlbCgxLjEpKSwKICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IHJlbCgwLjgpKSwKICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIHNpemUgPSByZWwoMS4xKSkKICAgICkKcHJpbnQoZ2dfb3ZlcmRpc3BfbG9yKQpgYGAKCgojIyBMT1JzIGJldHdlZW4gaW5mZXJlZCBhbmQgdHJ1ZSB2YWx1ZXMgZm9yICRcbXUkLgpgYGB7ciwgZmlnLndpZHRoPTIsIGZpZy5oZWlnaHQ9My41fQpvcHRzJHZhbF90aHJlc2ggPC0gMWUtMTAKdG1wIDwtIGNvcHkoc3VtbWFyeV9zdGF0cykgJT4lIC5bLCBjKCJmZWF0dXJlcyIsICJvcl9nYW1tYSIsICJjcGdfY292IikgOj0gTlVMTF0KdG9fcGxvdCA8LSB0bXBbLCBjKCJjZWxscyIsICJjcGdzIiwgImZlYXR1cmVfbmFtZSIsIAogICAgICAgICAgICAgICAgICAgIm11X3RydWUiLCAibXVfbWVkaWFuIiwgIm11X21sZSIpXSAlPiUKICAuWywgbXVfbWxlIDo9IGlmZWxzZShtdV9tbGUgPiBvcHRzJHZhbF90aHJlc2ggJiAKICAgICAgICAgICAgICAgICAgICAgICAgIG11X21sZSA8IDEgLSBvcHRzJHZhbF90aHJlc2gsIG11X21sZSwgCiAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKG11X21sZSA8IG9wdHMkdmFsX3RocmVzaCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9wdHMkdmFsX3RocmVzaCwgMSAtIG9wdHMkdmFsX3RocmVzaCkpXSAlPiUKICAuWywgbWxlX2xvciA6PSBhYnMoc2NNRVQ6OjouY29tcHV0ZV9sb2dfb2Rkc19yYXRpbyhtdV90cnVlLCBtdV9tbGUpKV0gJT4lCiAgLlssIHNjbWV0X2xvciA6PSBhYnMoc2NNRVQ6OjouY29tcHV0ZV9sb2dfb2Rkc19yYXRpbyhtdV90cnVlLCBtdV9tZWRpYW4pKV0gJT4lCiAgLlssIGMoIm11X3RydWUiLCAibXVfbWVkaWFuIiwgIm11X21sZSIsICJmZWF0dXJlX25hbWUiKSA6PSBOVUxMXSAlPiUKICBtZWx0KGlkLnZhcnMgPSBjKCJjZWxscyIsICJjcGdzIikpCgp0b19wbG90JGNwZ3MgPC0gZmFjdG9yKHRvX3Bsb3QkY3BncywgbGV2ZWxzID0gYyg4LCAxNSwgNTApLCAKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJDcEcgcG9vciIsICJDcEcgbW9kZXJhdGUiLCAiQ3BHIHJpY2giKSkKdG9fcGxvdCR2YXJpYWJsZSA8LSBmYWN0b3IodG9fcGxvdCR2YXJpYWJsZSwgbGV2ZWxzID0gYygibWxlX2xvciIsICJzY21ldF9sb3IiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkJCIE1MRSIsICJzY01FVCIpKQoKZ2dfbWVhbl9sb3IgPC0gZ2dib3hwbG90KHRvX3Bsb3QsIHggPSAiY2VsbHMiLCB5ID0gInZhbHVlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gInZhcmlhYmxlIiwgbHdkID0gMC40LCBvdXRsaWVyLnNpemUgPSAwLjUpICsKICBmYWNldF93cmFwKH5jcGdzLCBzY2FsZXMgPSAiZml4ZWQiLCBucm93ID0gMykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoICIjOTk5OTk5IiwgIiNFNjlGMDAiLCAiIzU2QjRFOSIpKSArCiAgbGFicyh0aXRsZSA9IE5VTEwsIHggPSAiTnVtYmVyIG9mIGNlbGxzIiwgCiAgICAgICB5ID0gZXhwcmVzc2lvbihwYXN0ZSgiTE9SICgiLCBtdVt0cnVlXSwgIiwgIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdVtlc3RpbWF0ZWRdLCAiKSIpKSwgZmlsbCA9ICJNb2RlbCIpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHlsaW0oYygwLCAxLjgpKSArCiAgdGhlbWUoCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGxlZ2VuZC5tYXJnaW4gPSBtYXJnaW4oLTIsIDAsIC0yLCAwKSwKICAgICAgbGVnZW5kLmJveC5tYXJnaW4gPSBtYXJnaW4oLTIsIDAsIC0yLCAwKSwKICAgICAgcGFuZWwuc3BhY2luZy55ID0gdW5pdCgzLCAibGluZXMiKSwKICAgICAgcGxvdC50YWcgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBmYWNlID0gImJvbGQiLCBzaXplID0gcmVsKDEuNikpLAogICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIHNpemUgPSByZWwoMS4xKSksCiAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBzaXplID0gcmVsKDEuMSkpLAogICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBzaXplID0gcmVsKDAuOCkpLAogICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IHJlbCgxLjEpKQogICAgKQpwcmludChnZ19tZWFuX2xvcikKYGBgCgoKIyBGaW5hbCBwbG90cwpgYGB7ciwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NH0KbGlicmFyeShwYXRjaHdvcmspCmdnX2dhbW1hIDwtICgoZ2dfb3ZlcmRpc3BbWzFdXS9nZ19vdmVyZGlzcFtbMl1dL2dnX292ZXJkaXNwW1szXV0pIHwgZ2dfb3ZlcmRpc3BfbG9yKSArIAogIHBsb3RfbGF5b3V0KHdpZHRocyA9IGMoMy43LCAxLjIpLCBoZWlnaHRzID0gYygxLjA4LCAxKSkgKwogIHBsb3RfYW5ub3RhdGlvbih0YWdfbGV2ZWxzID0gJ2EnKQpwcmludChnZ19nYW1tYSkKCnBkZihmaWxlID0gcGFzdGUwKG91dF9kaXIsICJzaHJpbmthZ2Vfb3ZlcmRpc3AucGRmIiksIHdpZHRoID0gMTAsIGhlaWdodCA9IDkpCnByaW50KGdnX2dhbW1hKQpkZXYub2ZmKCkKCmdnX211IDwtICgoZ2dfbWVhbltbMV1dL2dnX21lYW5bWzJdXS9nZ19tZWFuW1szXV0pIHwgZ2dfbWVhbl9sb3IpICsgCiAgcGxvdF9sYXlvdXQod2lkdGhzID0gYygzLjcsIDEuMiksICwgaGVpZ2h0cyA9IGMoMS4wOCwgMSkpICsKICBwbG90X2Fubm90YXRpb24odGFnX2xldmVscyA9ICdhJykKcHJpbnQoZ2dfbXUpCgpwZGYoZmlsZSA9IHBhc3RlMChvdXRfZGlyLCAic2hyaW5rYWdlX21lYW4ucGRmIiksIHdpZHRoID0gMTAsIGhlaWdodCA9IDkpCnByaW50KGdnX211KQpkZXYub2ZmKCkKYGBgCgo=